/*
 * File name: WatermarkManager.java
 * 
 * Description: The class provides a simple interface to the mosaic algorithm.
 *              The class allows passing watermark frame and picture captured to be
 *              stitched together, and then stitches and blends them together into
 *              a single output which can then be accessed as a single image. 
 *
 * Author: Theobald_wu, contact with wuqizhi@tydtech.com
 * 
 * Date: 2014-4-29   
 * 
 * Copyright (C) 2014 TYD Technology Co.,Ltd.
 * 
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.freeme.cameraplugin.watermarkmode;

import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONException;
import org.json.JSONObject;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.graphics.drawable.Drawable;
import android.graphics.Canvas;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.LayoutInflater.Factory;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import com.freeme.camera.BasePluginImpl;
import com.freeme.camera.CameraInterfaceManage;
import com.freeme.camera.CameraPluginUtil;
import com.freeme.camera.ICameraExt;
import com.freeme.camera.data.PictureSizeInfo;
import com.freeme.camera.data.Size;
import com.freeme.cameraplugin.watermarkmode.ui.Rotatable;
import com.freeme.cameraplugin.watermarkmode.model.WatermarkItemInfo;
import com.freeme.cameraplugin.watermarkmode.model.WatermarkResouceId;
import com.freeme.cameraplugin.watermarkmode.ui.RotataTextView;
import com.freeme.cameraplugin.watermarkmode.ui.RotateImageView;
import com.freeme.cameraplugin.watermarkmode.ui.RotateLayout;
import com.freeme.cameraplugin.watermarkmode.ui.WatermarkPreviewPanel;
import com.freeme.cameraplugin.watermarkmode.ui.WatermarkViewPager;
import com.freeme.cameraplugin.watermarkmode.utils.WatermarkUtil;
import com.zhuoyi.security.service.location.ILocationSecurityService;
import com.zhuoyi.security.service.location.LocationAgent;

public class WatermarkMode extends BasePluginImpl {
    private static final String TAG = "[TYD_DEBUG]WatermarkManager";
    private static final float PREVIEW_FADE_ALPHA = 0.10f;
    private static final float PREVIEW_LONG_PRESSED_ALPHA = 0.60f;
    private static final String SHOW_GUIDE_KEY = "pref.watermark.guide";
    /**
     * The output image size is 6M pixels and full-screen as according of
     * finding. Find process: Find the full-screen picture size that nearest 6M
     * at first, then find the sensor max supported picture size but not over 6M
     * if not found, and then use found for output image size. Such as 3072x1728
     * at QHD project if sensor support.
     */
    public final long OUTPUT_IMAGE_SIZE_IDEAL = 4 * 1024 * 1024;
    public static final int OUTPUT_IMAGE_WIDTH_IDEAL = 1920;
    public static final int OUTPUT_IMAGE_HEIGHT_IDEAL = 2560;

    public static final int OUTPUT_OFFSET_SIZE = 256;

    private int mOutputWidth;
    private int mOutputHeight;
    private float mOutputScale;

    public double MFULLSCREENRATIO = 4d / 3;
    private RotateLayout mPreview;
    private float mPreviewScale; // base on output
    private String mCurrentType;
    private int mOrientation = -1;
    private boolean mIsLandScape;
    private boolean mEnabled = true;
    private SharedPreferences mSharedPref;

    private String mAddr = "";
    private boolean mBlendingOutput = false;
    private String mCurrItemId;
    private LayoutInflater mLayoutInflater;

    private List<WatermarkItemInfo> mPreviewItemList = new ArrayList<WatermarkItemInfo>();

    private ICameraExt mCameraExt = null;

    private WatermarkPreviewPanel mPreviewPanel;
    private ViewGroup mRootView;
    private LocationAgent mLocationAgent;
    public ILocationSecurityService service;
    private LocationServiceConnection connection;
    protected String addr;
    private static final int PLUGIN_ID = 102;
    private String mPackageName;

    @Override
    public int getModuleID() {
        return PLUGIN_ID;
    }

    @Override
    public String getPkgName() {
        return mPackageName;
    }

    @Override
    public Drawable getModuleIcon() {
        return mContext.getResources().getDrawable(R.drawable.entry_icon);
    }

    @Override
    public String getModuleTitle() {
        return mContext.getString(R.string.app_name);
    }

    @SuppressWarnings("static-access")
    @Override
    public void showPanel(ViewGroup root) {
        if (root != null) {
            mCameraExt = CameraInterfaceManage.getInstance().cameraExt;
            show(root);
        }
    }

    @Override
    public void hidePanel() {
        hide();
    }

    @Override
    public void switchPanel(ViewGroup root) {

    }

    @Override
    public void onOrientationChanged(int orientation) {
        if (mCurrentType == null || isShowGuide()) {
            return;
        }
        if (mOrientation != orientation && !mBlendingOutput) {
            mOrientation = orientation;
            onOrientationChanged();
            Log.i("xueweili", "onOrientationChanged" + "onOrientationChanged");
        }
    }

    @Override
    protected void onRelease() {
        release();
    }

    @Override
    public PictureSizeInfo getPictureSizeInfo() {
        PictureSizeInfo info = new PictureSizeInfo(0, 0);
        info.setExpectScreenRatio(MFULLSCREENRATIO);
        info.setExpectOutImageSize(OUTPUT_IMAGE_SIZE_IDEAL);
        return info;
    }

    public String getCurrItemId() {
        return mCurrItemId;
    }

    public void setCurrItemId(String id) {
        mCurrItemId = id;
    }

    public void clearPreviewItemList() {
        mPreviewItemList.clear();
    }

    public int getCurrItemPageIndex() {
        int index = 0;
        for (int i = 0; i < mPreviewItemList.size(); i++) {
            WatermarkItemInfo itemInfo = mPreviewItemList.get(i);
            String id = String.valueOf(itemInfo.thumbnailId) + itemInfo.thumbnailUrl;
            if (mCurrItemId.equals(id)) {
                index = i;
                break;
            }
        }
        return index;
    }

    public void replacePreviewItem(WatermarkItemInfo itemInfo) {
        for (WatermarkItemInfo info : mPreviewItemList) {
            if (info.type.equals(itemInfo.type)) {
                info.layoutId = itemInfo.layoutId;
                info.colorId = itemInfo.colorId;
                info.thumbnailId = itemInfo.thumbnailId;
                info.primitiveId = itemInfo.primitiveId;
                info.promptId = itemInfo.promptId;
                info.color = itemInfo.color;
                info.thumbnailUrl = itemInfo.thumbnailUrl;
                info.primitiveUrl = itemInfo.primitiveUrl;
                info.prompt = itemInfo.prompt;
                info.type = itemInfo.type;
                info.name = itemInfo.name;
                break;
            }
        }
    }

    public WatermarkMode(Context c, String pkgName) {
        super(c, pkgName);
        mPackageName = pkgName;
        int orientation = mContext.getResources().getConfiguration().orientation;
        mIsLandScape = (Configuration.ORIENTATION_LANDSCAPE == orientation);
        mLayoutInflater = LayoutInflater.from(mContext);
        mLayoutInflater.setFactory(new Factory() {
            @Override
            public View onCreateView(String name, Context context, AttributeSet attrs) {
                if ("com.freeme.cameraplugin.watermarkmode.ui.RotateLayout".equals(name)) {
                    return new RotateLayout(context, attrs);
                } else if ("com.freeme.cameraplugin.watermarkmode.ui.WatermarkPreviewPanel"
                        .equals(name)) {
                    return new WatermarkPreviewPanel(context, attrs);
                } else if ("android.support.v4.view.ViewPager".equals(name)) {
                    return new ViewPager(context, attrs);
                } else if ("com.freeme.cameraplugin.watermarkmode.ui.WatermarkViewPager"
                        .equals(name)) {
                    return new WatermarkViewPager(context, attrs);
                } else if ("com.freeme.cameraplugin.watermarkmode.ui.RotateImageView".equals(name)) {
                    return new RotateImageView(context, attrs);
                } else if ("com.freeme.cameraplugin.watermarkmode.ui.RotataTextView".equals(name)) {
                    return new RotataTextView(context, attrs);
                }
                return null;
            }
        });
        initLocation();
        // IntentFilter filter = new IntentFilter();
        // filter.addAction("android.action.freeme.ec.item.add");
        // filter.addAction("android.action.freeme.ec.item.del");
        // mContext.registerReceiver(mItemChangedReceiver, filter);
    }

    private void initWaterMark() {
        initPreviewPanel();
    }

    public boolean readOfflineNewStatus(String key, boolean defaultVal) {
        return mSharedPref.getBoolean(key, defaultVal);
    }

    public void saveOfflineNewStatus(String key, boolean value) {
        SharedPreferences.Editor editor = mSharedPref.edit();
        editor.putBoolean(key, value);
        editor.commit();
    }

    public float getPreviewScale() {
        return mPreviewScale;
    }

    /**
     * update watermark type and render preview
     * 
     * @param type different style
     */
    public void render(final String type) {
        Log.i(TAG, "render(" + type + ")...");
        mOrientation = mCameraExt.getOrientation();
        if (mOrientation == -1) {
            mOrientation = 0;
        }
        setOrientation(mPreviewPanel, mOrientation, true);
        updateWatermark(type);
    }

    /**
     * Stitches and blends watermark frame and picture captured together into a
     * single output which can then be accessed as a single image.
     * 
     * @param rawData captured picture raw data
     * @return blend result data
     */
    @Override
    public byte[] blendOutput(byte[] rawData) {
        if (mCurrentType == null || rawData == null || mOutputWidth == 0 || mOutputHeight == 0) {
            // do nothing
            Log.i(TAG, "blendOutput(): do nothing, mCurrentType = " + mCurrentType);
            mBlendingOutput = false;
            return null;
        }
        mBlendingOutput = true;
        synchronized (WatermarkMode.this) {
            // creat output
            long time = System.currentTimeMillis();
            View panel = mPreviewPanel.getOutputPanel(mOrientation);
            if (panel == null) {
                mBlendingOutput = false;
                return null;
            }
            long getPannelTime = System.currentTimeMillis();
            android.util.Log.i("keke", "blendOutput getOutputPanel spent time:"
                    + (getPannelTime - time));
            Bitmap output = Bitmap.createBitmap(mOutputWidth, mOutputHeight, Config.ARGB_8888);
            if (output == null) {
                // out of memory
                Log.e(TAG, "OOM, output = null, blendOutput() fail!");
                mBlendingOutput = false;
                return null;
            } else {
                Canvas canvas = new Canvas(output);
                canvas.save();
                canvas.scale(mOutputScale, mOutputScale);
                canvas.translate(0, 0);
                panel.draw(canvas);
                canvas.restore();
            }
            long createBitmapTime = System.currentTimeMillis();
            android.util.Log.i("keke", "blendOutput createBitmapTime spent time:"
                    + (createBitmapTime - getPannelTime));
            // picture captured
            Bitmap src_bitmap = null;
            try {

                Options options = new Options();
                options.inMutable = true;
                src_bitmap = BitmapFactory.decodeByteArray(rawData, 0, rawData.length, options);
            } catch (OutOfMemoryError e) {
                Log.e(TAG, "blendOutput() fail!", e);
                mBlendingOutput = false;
                return null;
            }

            if (src_bitmap == null) {
                Log.e(TAG, "src_bitmap = null, blendOutput() fail!");
                mBlendingOutput = false;
                return null;
            }
            long decodeTime = System.currentTimeMillis();
            android.util.Log.i("keke", "blendOutput decodeTime spent time:"
                    + (decodeTime - createBitmapTime));
            Canvas canvas = new Canvas(src_bitmap);
            output = CameraPluginUtil.rotate(output, mOrientation);
            canvas.drawBitmap(output, 0, 0, null);
            canvas.save(Canvas.ALL_SAVE_FLAG);
            canvas.restore();
            long drawTime = System.currentTimeMillis();
            android.util.Log
                    .i("keke", "blendOutput drawTime spent time:" + (drawTime - decodeTime));
            // compress new JPEG file to save
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            src_bitmap.compress(Bitmap.CompressFormat.JPEG, 90, baos);
            rawData = baos.toByteArray();
            long blendTime = System.currentTimeMillis();
            android.util.Log
                    .i("keke", "blendOutput blendTime spent time:" + (blendTime - drawTime));
            output.recycle();
            src_bitmap.recycle();
            mBlendingOutput = false;
            return rawData;
        }
    }

    /**
     * whether blending output
     * 
     * @return
     */
    public boolean isBlendingOutput() {
        return mBlendingOutput;
    }

    public void setBlendingState() {
        if (mCurrentType != null) {
            mBlendingOutput = true;
        }
    }

    /**
     * Whether affect watermark
     * 
     * @return
     */
    public boolean isOn() {
        boolean result = false;

        return result;
    }

    public void hide() {
        mCurrentType = null;
        if (mPreviewPanel == null || mRootView == null) {
            return;
        }
        mRootView.removeView(mPreviewPanel);
        mPreviewPanel.destroyDrawingCache();
        mPreviewPanel = null;
    }

    public void show(ViewGroup root) {
        mRootView = root;
        Log.i(TAG, "show");
        updateOutputSize();
        initWaterMark();
        render("watermark_0_1");
        onOrientationChanged();
    }

    public void refactor() {
        int orientation = mContext.getResources().getConfiguration().orientation;
        mIsLandScape = (Configuration.ORIENTATION_LANDSCAPE == orientation);
        initPreviewPanel();
        mPreviewPanel.init(mCameraExt, this);
        invalidate();
        Log.i(TAG, "refactor(): mIsLandScape = " + mIsLandScape);
    }

    public void onSwitchCamera() {
        // reset when switch camera
        mCurrentType = null;
    }

    @SuppressWarnings("unused")
    private void handlePreviewDataChanged() {
        if (mPreviewItemList != null) {
            for (WatermarkItemInfo info : mPreviewItemList) {
                if (info.thumbnailId == 0 || info.primitiveId == 0) {
                    boolean isExist = WatermarkUtil.isFileExistByFileUrl(info.thumbnailUrl);
                    if (isExist) {
                        isExist = WatermarkUtil.isFileExistByFileUrl(info.primitiveUrl);
                    }

                    if (!isExist) {
                        String type = info.type;
                        List<String> tags = WatermarkResouceId.PAGE_ARRAY_TAG.get(type);
                        String tag = tags.get(0);
                        WatermarkItemInfo itemInfo = WatermarkResouceId.ITEM_RESOURCE_IDS.get(tag);
                        itemInfo.setType(type);
                        String id = String.valueOf(info.thumbnailId) + info.thumbnailUrl;
                        if (id.equals(mCurrItemId)) {
                            mCurrItemId = String.valueOf(itemInfo.thumbnailId)
                                    + itemInfo.thumbnailUrl;
                        }
                        info.layoutId = itemInfo.layoutId;
                        info.colorId = itemInfo.colorId;
                        info.thumbnailId = itemInfo.thumbnailId;
                        info.primitiveId = itemInfo.primitiveId;
                        info.promptId = itemInfo.promptId;
                        info.color = itemInfo.color;
                        info.thumbnailUrl = itemInfo.thumbnailUrl;
                        info.primitiveUrl = itemInfo.primitiveUrl;
                        info.prompt = itemInfo.prompt;
                        info.type = itemInfo.type;
                    }
                }
            }
        }
    }

    public void updateWatermark(String type) {
        // if (!(type.equals(mCurrentType)) || mForceUpdateOutput) {
        // watermark type changed, should create new output.
        mCurrentType = type;
        invalidate();
        // }
    }

    public void fade(boolean faded) {
        if (mCurrentType == null) {
            return;
        }

        float alpha = 1;
        if (faded) {
            alpha = PREVIEW_FADE_ALPHA;
            setEnabled(false);
        } else {
            setEnabled(true);
        }

        mPreview.setAlpha(alpha);
    }

    public void longPressPreview(boolean pressed) {
        if (mCurrentType == null) {
            return;
        }

        float alpha = 1;
        if (pressed) {
            alpha = PREVIEW_LONG_PRESSED_ALPHA;
        }

        mPreview.setAlpha(alpha);

    }

    public boolean disappearGuide() {
        if (mCurrentType == null) {
            return false;
        }

        if (isShowGuide()) {
            mPreviewPanel.hideGuide();

            SharedPreferences.Editor editor = mSharedPref.edit();
            editor.putBoolean(SHOW_GUIDE_KEY, false);
            editor.apply();

            return true;
        }

        return false;
    }

    public boolean isShowGuide() {
        // boolean isShow = mSharedPref.getBoolean(SHOW_GUIDE_KEY, false);
        return false;
    }

    private void invalidate() {
        if (mCurrentType == null) {
            return;
        }

        if (mPreviewPanel.getVisibility() != View.VISIBLE) {
            mPreviewPanel.setVisibility(View.VISIBLE);
        }
    }

    private void updateOutputSize() {

        Size size = mCameraExt.getPictureSize();
        if (size == null) {
            return;
        }
        DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();

        if (mIsLandScape) {
            mOutputWidth = size.width;
            mOutputHeight = size.height;
        } else {
            mOutputHeight = size.width;
            mOutputWidth = size.height;
        }

        mOutputScale = Math.min(mOutputWidth, mOutputHeight) * 1.0f / OUTPUT_IMAGE_WIDTH_IDEAL;
        mPreviewScale = Math.min(metrics.widthPixels, metrics.heightPixels) * 1.0f
                / Math.min(mOutputWidth, mOutputHeight) * mOutputScale;

        Log.i(TAG, "updateOutputSize(): output width = " + mOutputWidth + ", output height = "
                + mOutputHeight + ", output scale = " + mOutputScale + ", preview panel scale = "
                + mPreviewScale);
    }

    /**
     * Show setting list view pager
     */
    public void popupSettingPanel() {
    }

    public String getCurrentAddr() {
        return mAddr;
    }

    public void setEnabled(boolean enabled) {
        mEnabled = enabled;
        mPreview.setEnabled(enabled);
    }

    public boolean isEnabled() {
        return mEnabled;
    }

    @SuppressLint("InflateParams")
    public void initPreviewPanel() {
        if (mRootView == null) {
            return;
        }
        if (mPreviewPanel == null) {
            mPreviewPanel = (WatermarkPreviewPanel) mLayoutInflater.inflate(
                    R.layout.watermark_preview_page_panel, null);
            mPreview = (RotateLayout) mPreviewPanel.findViewById(R.id.panel);
            RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
                    RelativeLayout.LayoutParams.MATCH_PARENT,
                    RelativeLayout.LayoutParams.WRAP_CONTENT);
            lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
            lp.addRule(RelativeLayout.CENTER_HORIZONTAL);
            mRootView.addView(mPreviewPanel, lp);
        }
        mPreviewPanel.init(mCameraExt, this);

    }

    private void setOrientation(View view, int orientation, boolean animation) {
        if (view == null) {
            return;
        }
        if (view instanceof Rotatable) {
            ((Rotatable) view).setOrientation(orientation, animation);
        } else if (view instanceof ViewGroup) {
            ViewGroup group = (ViewGroup) view;
            for (int i = 0, count = group.getChildCount(); i < count; i++) {
                setOrientation(group.getChildAt(i), orientation, animation);
            }
        }
    }

    public void onOrientationChanged() {
        if (mEnabled) {
            setOrientation(mPreviewPanel, mOrientation, true);
        }
    }

    public void release() {
        if (mRootView != null) {
            mRootView.removeView(mPreviewPanel);
        }
        mContext.unbindService(connection);
        mPreviewPanel = null;
        mPreview = null;
    }

    private void initLocation() {
        connection = new LocationServiceConnection();
        mLocationAgent = LocationAgent.getInstance();
        Intent intent = new Intent("com.zhuoyi.security.service.location.ACTION");
        mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

    class LocationServiceConnection implements ServiceConnection {
        public void onServiceConnected(ComponentName name, IBinder boundService) {
            service = ILocationSecurityService.Stub.asInterface((IBinder) boundService);
            try {
                PackageManager packageManage = mContext.getPackageManager();
                PackageInfo info = packageManage.getPackageInfo(mContext.getPackageName(), 0);
                mLocationAgent.setCallback(service, info, mLocationListener);
            } catch (Exception e) {

            }
            try {
                service.getLocationStrFromSecurity();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        public void onServiceDisconnected(ComponentName name) {
            service = null;
        }
    }

    public LocationAgent.LoacationCallBack mLocationListener = new LocationAgent.LoacationCallBack() {

        @Override
        public void getLocationStrByCooCompleted(String locationInfo) {
        }

        @Override
        public void getLocationStrCompleted(String locationInfo) {
            onLocationGot(locationInfo);
        }

        @Override
        public void getCoordinatesCompleted(String locationInfo) {
            onLocationGot(locationInfo);
        }

        public void onLocationGot(String locationInfo) {
            if (locationInfo == null || locationInfo.isEmpty()) {
                return;
            }
            try {
                JSONObject jsonObj = new JSONObject(locationInfo);
                String city = jsonObj.getString("city");
                String dist = jsonObj.getString("dist");
                mAddr = "";
                mAddr += city + "." + dist;
                mAddr = mAddr.replace("省", "");
                mAddr = mAddr.replace("市", "");
                mAddr = mAddr.replace("县", "");
                mAddr = mAddr.replace("区", "");
            } catch (JSONException e) {
            }
        }

    };
}
